Skip to content

Conversation

@bartgol
Copy link
Contributor

@bartgol bartgol commented Nov 4, 2025

Motivation

I toyed over the weekend with some code to enable expression templates in eamxx. Then I thought that it was maybe easier to work directly with views, so I moved to ekat. The framework should be lightweight in terms of performance, but I haven't tested it yet. I tested an earlier version in eamxx, which had more runtime overhead, and it was ~50% slower than a big manual loop. I am hoping that this ekat version (fully templated) will be faster and closer to parity with a manual loop.

The framework allows to create expressions from views and scalar, and then evaluate them (stuffing the result in a view). Something of the form

// x, y, and z are all views of the same shape
auto expression = x*exp(-y) - 2*sin(y);
evaluate(expression, z);

The first line creates an "expression", which is a compile-time evaluation tree. The second line performs a flat || for to evaluate it (uses RangePolicy in 1d, and MDRangePolicy otherwise).

So far, it only supports 1d,2d,and 3d views, and always evaluates the result as a double. If needed, I think we can easily expand to a generic type, at the cost of increasing the template signature of each class.

Testing

Added a unit test for 1d,2d,3d. Performs binary ops, simple math fcns, and ternary-operator-like evaluations.

Additional Information

I went back and forth between a couple of impl choices, namely whether eval should accept indices (like eval(int i, int j)) or accept no inputs, but require to set the evaluation data in the expression at each iteration. I think the former is prob faster, so I went with that, at the cost of increasing the number of methods to implement.

A few things I'd like to try:

  • Add support for ViewBroadcast: would allow do multiply a 2d view with a 3d one, along the proper indices. This may require additional info on what each index represent though, which adds complexity (but maybe not too much).
  • Add support for 1d reductions: e.g., do something like z = x + reduction(y,1), which reduces y along 2nd dim, then adds to x.
  • Linked to the above: if inner evaluations need to do a bit of work to compute each entry of the output (like reduction(y,1)), it would help to have evaluate switch betwen range and team policies, based on what expresison needs. This would require the expression to expose some "policy data", which could be something like "range is fine", or "needs team". This can prob be done at compile time, as each expression knows at compile time what kind of work it does, and the binary ops that compose expressions can ping inner expressions for this info.
  • Remove the hard-coded Real as the return type of the eval methods. This may require that all expressions be based on their return type, and the return type of compositions is then chosen based on promotion rules.

@bartgol bartgol requested a review from tcclevenger November 4, 2025 22:47
@bartgol bartgol self-assigned this Nov 4, 2025
@bartgol
Copy link
Contributor Author

bartgol commented Nov 4, 2025

We may decide that offering overloads like BinaryOp operator+(ViewT&,ViewT&) is a bit too much, as we are effectively overloading operators for a class we are not developing. Maybe we can assume that users create a ViewExpression first, like so

auto xe = view_expression(x);
auto ye = view_expression(y);
auto expression = xe*exp(-ye) - 2*sin(ye);

This would keep our overloads working on OUR types (plus scalars). It would also reduce the number of overloads that need to be implemented... Thoughts?

@bartgol bartgol force-pushed the bartgol/expression-templates branch 2 times, most recently from af1399b to e406d7d Compare November 5, 2025 18:16
@bartgol bartgol force-pushed the bartgol/expression-templates branch from e406d7d to 7ff4a3f Compare November 5, 2025 18:19
@bartgol
Copy link
Contributor Author

bartgol commented Nov 5, 2025

Notes: adding support for all ranks 0-8 was actually quite easy, so I just did it. Also, I was able to easily remove the hard-coding of the return type as Real. It required adding a small static fcn in each expression, specifying what the return type is, so that upstream expressions (like BinOp or math fcns) can infer their return type.

@bartgol bartgol force-pushed the bartgol/expression-templates branch from 7ff4a3f to 91f3975 Compare November 5, 2025 18:22
@bartgol bartgol force-pushed the bartgol/expression-templates branch from 4d8d992 to 37d63f9 Compare November 5, 2025 23:17
@bartgol
Copy link
Contributor Author

bartgol commented Nov 5, 2025

Updates:

  • Removed ScalarExpression. Instead, make BinaryExpression, CmpExpression, and ConditionalExpression handle the scenario where one of the two template types is NOT an expression, in which case it gets treated as a scalar.
  • Removed the overloads of binary ops and math fcns that accept a Kokkos view. Instead, the user MUST create a ViewExpression first, and use that in the following expressions. This is to avoid overstepping into kokkos' territory, overloading operators for their types.

These mods remarkably reduce the number of overloads we need to implement/maintain, at the small cost of a couple of if constexpr (hence, compiled away) in the eval method implementation.

class Expression {
public:

int num_indices () const { return cast().num_indices(); }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be turned into a static fcn, as the number of indices used by an expression is fully determined by its type.

So far, this fcn is only used in the evaluate call, to make sure that the rank of the view passed to the evaluation fcn is compatible with the expression to evaluate. I may revisit this pattern in a follow-up pr...

template<typename ELeft, typename ERight>
class CmpExpression : public Expression<CmpExpression<ELeft,ERight>> {
public:
using ret_t = int;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used int for some reason, but it could probably be changed into bool. I already have a follow up branch where I extend the expressions framework to work with Packs, so it can be changed there.

@bartgol bartgol merged commit b2e66d0 into master Nov 6, 2025
4 checks passed
@bartgol bartgol deleted the bartgol/expression-templates branch November 6, 2025 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants